﻿/* ######################################## *\
 * ### Copyright (C) 2009 AJ Ravindiran ### *
 * ### HTTP://THEAJ.NET/ <AJ@THEAJ.NET> ### *
\* ######################################## */
using System;
using System.Text;

namespace AJRavindiran.Jolt.RuneScape.Network.Codecs
{
    /// <summary>
    /// Represents a Decoder which provides reading methods from a client.
    /// </summary>
    public class Decoder
    {
        #region Fields
        private int mReadCapacity;
        private int mInputOffset = 0;
        private byte[] mInputBuffer;
        #endregion

        #region Constructors
        public Decoder(int inCapacity)
        {
            this.mReadCapacity = inCapacity;
            this.mInputBuffer = new byte[inCapacity];
        }
        #endregion

        #region Properties
        public int ReadCapacity
        {
            get { return mReadCapacity; }
            set { mReadCapacity = value; }
        }

        public int InputOffset
        {
            get { return mInputOffset; }
            set { mInputOffset = value; }
        }

        public byte[] InputBuffer
        {
            get { return mInputBuffer; }
            set { mInputBuffer = value; }
        }
        #endregion

        #region Methods
        public byte ReadSignedByte()
        {
            return (byte)(mInputBuffer[mInputOffset++]);
        }

        public int ReadUnsignedByte()
        {
            return (ReadSignedByte() & 0xff);
        }

        public byte ReadSignedByteA()
        {
            return (byte)(ReadSignedByte() - 128);
        }

        public byte ReadSignedByteC()
        {
            return (byte)(-ReadSignedByte());
        }

        public byte ReadSignedByteS()
        {
            return (byte)(128 - ReadSignedByte());
        }

        public int ReadUnsignedByteA()
        {
            return (ReadUnsignedByte() - 128 & 0xff);
        }

        public int ReadUnsignedByteC()
        {
            return -(ReadUnsignedByte() & 0xff);
        }

        public int ReadUnsignedByteS()
        {
            return (128 - ReadUnsignedByte() & 0xff);
        }

        public void ReadBytes(byte[] abyte0, int i, int j)
        {
            for (int k = j; k < j + i; k++)
            {
                abyte0[k] = ReadSignedByte();
            }
        }

        public void ReadBytes_reverse(byte[] abyte0, int i, int j)
        {
            for (int k = (j + i) - 1; k >= j; k--)
            {
                abyte0[k] = ReadSignedByte();
            }
        }

        public void ReadBytes_reverseA(byte[] abyte0, int i, int j)
        {
            for (int k = (j + i) - 1; k >= j; k--)
            {
                abyte0[k] = ReadSignedByteA();
            }
        }

        public int ReadSignedWordBigEndian()
        {
            mInputOffset += 2;
            int i = ((mInputBuffer[mInputOffset - 1] & 0xff) << 8) +
                (mInputBuffer[mInputOffset - 2] & 0xff);
            if (i > 32767)
            {
                i -= 0x10000;
            }
            return i;
        }

        public int ReadSignedWordA()
        {
            mInputOffset += 2;
            int i = ((mInputBuffer[mInputOffset - 2] & 0xff) << 8) +
                (mInputBuffer[mInputOffset - 1] - 128 & 0xff);
            if (i > 32767)
            {
                i -= 0x10000;
            }
            return i;
        }

        public int ReadSignedWordBigEndianA()
        {
            mInputOffset += 2;
            int i = ((mInputBuffer[mInputOffset - 1] & 0xff) << 8) +
                (mInputBuffer[mInputOffset - 2] - 128 & 0xff);
            if (i > 32767)
            {
                i -= 0x10000;
            }
            return i;
        }

        public int ReadUnsignedWordBigEndian()
        {
            mInputOffset += 2;
            return ((mInputBuffer[mInputOffset - 1] & 0xff) << 8) +
                (mInputBuffer[mInputOffset - 2] & 0xff);
        }

        public int ReadUnsignedWord()
        {
            mInputOffset += 2;
            return ((mInputBuffer[mInputOffset - 2] & 0xff) << 8) +
                (mInputBuffer[mInputOffset - 1] & 0xff);
        }

        public int ReadUnsignedWordA()
        {
            mInputOffset += 2;
            return ((mInputBuffer[mInputOffset - 2] & 0xff) << 8) +
                (mInputBuffer[mInputOffset - 1] - 128 & 0xff);
        }

        public int ReadUnsignedWordBigEndianA()
        {
            mInputOffset += 2;
            return ((mInputBuffer[mInputOffset - 1] & 0xff) << 8) +
                (mInputBuffer[mInputOffset - 2] - 128 & 0xff);
        }

        public int ReadDWord_v1()
        {
            mInputOffset += 4;
            return ((mInputBuffer[mInputOffset - 2] & 0xff) << 24) +
                ((mInputBuffer[mInputOffset - 1] & 0xff) << 16) +
                ((mInputBuffer[mInputOffset - 4] & 0xff) << 8) +
                (mInputBuffer[mInputOffset - 3] & 0xff);
        }

        public int ReadDWord_v2()
        {
            mInputOffset += 4;
            return ((mInputBuffer[mInputOffset - 3] & 0xff) << 24) +
                ((mInputBuffer[mInputOffset - 4] & 0xff) << 16) +
                ((mInputBuffer[mInputOffset - 1] & 0xff) << 8) +
                (mInputBuffer[mInputOffset - 2] & 0xff);
        }

        public int ReadSignedWord()
        {
            mInputOffset += 2;
            int i = ((mInputBuffer[mInputOffset - 2] & 0xff) << 8) +
                (mInputBuffer[mInputOffset - 1] & 0xff);
            if (i > 32767)
            {
                i -= 0x10000;
            }
            return i;
        }

        public int ReadDWord()
        {
            mInputOffset += 4;
            return ((mInputBuffer[mInputOffset - 4] & 0xff) << 24) +
                ((mInputBuffer[mInputOffset - 3] & 0xff) << 16) +
                ((mInputBuffer[mInputOffset - 2] & 0xff) << 8) +
                (mInputBuffer[mInputOffset - 1] & 0xff);
        }

        public long ReadQWord()
        {
            long l = (long)ReadDWord() & 0xffffffffL;
            long l1 = (long)ReadDWord() & 0xffffffffL;
            return (l << 32) + l1;
        }

        public long ReadQWord2()
        {
            mInputOffset += 8;
            return (((mInputBuffer[mInputOffset - 8] & 0xff) << 56) +
                ((mInputBuffer[mInputOffset - 7] & 0xff) << 48) +
                ((mInputBuffer[mInputOffset - 6] & 0xff) << 40) +
                ((mInputBuffer[mInputOffset - 5] & 0xff) << 32) +
                ((mInputBuffer[mInputOffset - 4] & 0xff) << 24) +
                ((mInputBuffer[mInputOffset - 3] & 0xff) << 16) +
                ((mInputBuffer[mInputOffset - 2] & 0xff) << 8) +
                (mInputBuffer[mInputOffset - 1] & 0xff));
        }

        public string ReadString()
        {
            StringBuilder sb = new StringBuilder();
            byte b;
            while ((b = ReadSignedByte()) != 0)
                sb.Append((char)b);
            return sb.ToString();
        }

        public int Read3Bytes()
        {
            return (ReadUnsignedByte() << 16) |
                (ReadUnsignedByte() << 8) | ReadUnsignedByte();
        }

        public int Read2Bytes()
        {
            return (ReadUnsignedByte() << 8) + ReadUnsignedByte();
        }
        #endregion
    }
}
